package cn.anecansaitin.firecrafting.common.crafting;

import cn.anecansaitin.firecrafting.ModConstant;
import cn.anecansaitin.firecrafting.api.client.crafting.IJEIRecipeRender;
import cn.anecansaitin.firecrafting.client.crafting.FireEffectRecipeRender;
import cn.anecansaitin.firecrafting.common.Config;
import cn.anecansaitin.firecrafting.common.util.ModJsonReader;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class FireEffectRecipe extends SimpleFireRecipe {
    private final List<MobEffectInstance> outputs;
    private final int radius;

    public FireEffectRecipe(ResourceLocation id, ImmutableList<Block> fireBlock, NonNullList<Ingredient> inputs, List<MobEffectInstance> outputs, int radius) {
        super(id, fireBlock, inputs);
        this.outputs = outputs;
        this.radius = radius;
    }

    @Override
    public boolean doAction(List<ItemStack> stacks, BlockPos pos, Level world) {
        if (match(stacks, pos, world)) {
            world.getEntitiesOfClass(LivingEntity.class, new AABB(pos.above(radius).east(radius).south(radius), pos.below(radius).west(radius).north(radius)))
                    .forEach((e) -> outputs.forEach(pEffectInstance -> e.addEffect(new MobEffectInstance(pEffectInstance))));
            ((ServerLevel)world).sendParticles(ParticleTypes.EFFECT, pos.getX(), pos.getY(), pos.getZ(), 20, 2, 2, 2, 4);
            return true;
        }
        return false;
    }

    @Override
    public IJEIRecipeRender getRender() {
        return FireEffectRecipeRender.INSTANCE;
    }

    public List<MobEffectInstance> getOutputs() {
        return outputs;
    }

    public int getRadius() {
        return radius;
    }

    @Override
    public @NotNull RecipeSerializer<?> getSerializer() {
        return ModRecipeTypes.FIRE_EFFECT_SERIALIZER.get();
    }

    //序列化器实现类
    public static class Serializer extends ForgeRegistryEntry<RecipeSerializer<?>> implements RecipeSerializer<FireEffectRecipe> {
        public static final String NAME = ModConstant.MOD_ID + ":fire_effect_crafting";

        //从json读取配方
        @Override
        public @NotNull FireEffectRecipe fromJson(@NotNull ResourceLocation id, @NotNull JsonObject json) {
            //获取配方物品成分
            NonNullList<Ingredient> nonnulllist = ModJsonReader.ingredientsFromJson(GsonHelper.getAsJsonArray(json, "ingredients"));
            //判断配方成分的可用性
            if (nonnulllist.isEmpty()) {
                throw new JsonParseException("火焰燃烧(效果)配方内没有任何原料 No ingredients for fire block recipe");
            } else if (nonnulllist.size() > Config.WORLD_MAX_COUNT.get()) {
                throw new JsonParseException("火焰燃烧(效果)配方内的原料超出上限" + Config.WORLD_MAX_COUNT.get() + "  Too many ingredients for fire block recipe. The maximum is " + Config.WORLD_MAX_COUNT.get());
            }
            //读取效果
            ImmutableList<MobEffectInstance> output = ImmutableList.copyOf(ModJsonReader.effectFromJson(json, "output"));
            if (output.isEmpty()) {
                throw new JsonParseException("火焰燃烧(效果)配方内没有任何产物 No output for fire block recipe");
            }
            //读取半径
            int radius = json.get("radius").getAsInt();
            //获取可用火焰
            ImmutableList<Block> fire = ImmutableList.copyOf(ModJsonReader.blocksFromJson(json, "fire"));
            //判断火焰可用性
            if (fire.isEmpty()) {
                throw new JsonParseException("火焰燃烧(效果)配方内没有指定火焰 No fire block for fire block recipe");
            }
            return new FireEffectRecipe(id, fire, nonnulllist, output, radius);
        }

        //从网络读取配方
        @Nullable
        @Override
        public FireEffectRecipe fromNetwork(@NotNull ResourceLocation id, @NotNull FriendlyByteBuf buffer) {
            //获取火焰数量
            int fireCount = buffer.readVarInt();
            //创建list构造器
            ImmutableList.Builder<Block> fireBuilder = ImmutableList.builder();
            //从buffer中读取火焰
            for (int i = 0; i < fireCount; i++) {
                fireBuilder.add(Objects.requireNonNull(ForgeRegistries.BLOCKS.getValue(buffer.readResourceLocation())));
            }
            //获取产物效果
            ArrayList<MobEffectInstance> list = buffer.readCollection(ArrayList::new, (b) -> MobEffectInstance.load(Objects.requireNonNull(b.readNbt())));
            ImmutableList<MobEffectInstance> output = ImmutableList.copyOf(list);
            //获取半径
            int radius = buffer.readVarInt();
            //从buffer中获取配方成分数量并创建集合
            NonNullList<Ingredient> input = NonNullList.withSize(buffer.readVarInt(), Ingredient.EMPTY);
            //从buffer中获取指定数量成分
            for (int i = 0; i < input.size(); i++) {
                input.set(i, Ingredient.fromNetwork(buffer));
            }
            return new FireEffectRecipe(id, fireBuilder.build(), input, output, radius);
        }

        //将配方传入网络
        @Override
        public void toNetwork(FriendlyByteBuf buffer, FireEffectRecipe recipe) {
            //获取配方火焰
            ImmutableList<Block> fireBlock = recipe.fireBlock;
            //向buffer写入火焰数量
            buffer.writeVarInt(fireBlock.size());
            //向buffer写入火焰
            fireBlock.forEach((b) -> buffer.writeResourceLocation(Objects.requireNonNull(b.getRegistryName())));
            //写入配方产物效果
            buffer.writeCollection(recipe.outputs, (b, e) -> b.writeNbt(e.save(new CompoundTag())));
            //写入半径
            buffer.writeVarInt(recipe.radius);
            //获取配方成分
            NonNullList<Ingredient> inputs = recipe.inputs;
            //向buffer写入成分数量
            buffer.writeVarInt(inputs.size());
            //向buffer写入成分
            inputs.forEach((i) -> i.toNetwork(buffer));
        }
    }
}
